package moon.core.genericity;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;

import moon.core.Model;
import moon.core.Name;
import moon.core.classdef.ClassDef;
import moon.core.classdef.MethDec;
import moon.core.classdef.Type;

/**
 * Formal parameter.
 * 
 * @author <a href="mailto:rmartico@ubu.es">Ra&uacute;l Marticorena</a>
 * 
 */
@SuppressWarnings("serial")
public abstract class FormalPar extends Type {

	/**
	 * Separator.
	 */
	private static final String SEPARATOR = "@";

	/**
	 * Class def where the formal par is declared.
	 */
	private ClassDef classDef;

	/**
	 * Method declaration where the formal parameter is declared.
	 */
	private MethDec methDec;

	/**
	 * Gets the class that contains the formal parameter or null if it is
	 * contained in a method.
	 * 
	 * @return class that contains the formal parameter
	 * @see #setClassDef
	 */
	public ClassDef getClassDef() {
		return this.classDef;
	}

	/**
	 * Sets the class that contains the formal parameter.
	 * 
	 * @param classDef
	 *            class
	 * @see #getClassDef
	 */
	public void setClassDef(ClassDef classDef) {
		this.classDef = classDef;
	}

	/**
	 * Gets the method that contains the formal parameter or null if it is
	 * contained in the class.
	 * 
	 * @return method declaration
	 * @see #setMethDec
	 */
	public MethDec getMethDec() {
		return this.methDec;
	}

	/**
	 * Constructor.
	 * 
	 * @param name
	 *            name
	 */
	public FormalPar(Name name) {
		super(name);
	}

	/**
	 * Constructor.
	 * 
	 * @param name
	 *            name
	 * @param classDef
	 *            class
	 */
	public FormalPar(Name name, ClassDef classDef) {
		super(name);
		this.classDef = classDef;
	}

	/**
	 * Constructor.
	 * 
	 * @param name
	 *            name
	 * @param methDec
	 *            method
	 */
	public FormalPar(Name name, MethDec methDec) {
		super(name);
		this.methDec = methDec;
	}

	/**
	 * 
	 * Implements abstract method NamedObject.getUniqueName. {@inheritDoc}
	 * 
	 * Name convention: uniqueNameOfTheClass@name
	 */
	@Override
	public Name getUniqueName() {
		if (this.classDef != null) {
			return this.getClassDef().getUniqueName()
					.concat(SEPARATOR + this.getName().toString());
		} else {
			return this.getMethDec().getUniqueName()
					.concat(SEPARATOR + this.getName().toString());
		}
	}

	/**
	 * Sets the method that contains the formal parameter.
	 * 
	 * @param methDec
	 *            method
	 * @see #getMethDec
	 */
	public void setMethDec(MethDec methDec) {
		this.methDec = methDec;
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @return {@inheritDoc}
	 */
	@Override
	public boolean isComplete() {
		return false;
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @return {@inheritDoc}
	 */
	@Override
	public boolean isParametricType() {
		return false;
	}
	
	/**
	 * {@inheritDoc}.
	 * 
	 * @param other
	 *            {@inheritDoc}
	 * @return {@inheritDoc}
	 */
	@Override
	public boolean equals(Object other) {
		if (other != null && !(other instanceof FormalPar)) {
			throw new ClassCastException();
		}
		FormalPar fp2 = (FormalPar) other;
		if (fp2 != null
				&& this.getName().toString()
						.compareTo(fp2.getName().toString()) == 0) {
			return true;
		}
		return false;
	}

	/**
	 * Gets the type code.
	 * 
	 * @return type code
	 */
	@Override
	public String getTypeCode() {
		return this.getName().toString();
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @return {@inheritDoc}
	 */
	public List<FormalPar> getRelatedFormalParameters() {
		List<FormalPar> list = new ArrayList<FormalPar>();
		list.add(this);
		return list;
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @param type
	 *            {@inheritDoc}
	 * @return {@inheritDoc}
	 */
	public Type getTransformedType(Type type) {
		// Without transformation
		return type;
	}

	/**
	 * {@inheritDoc}.
	 * 
	 * @param type {@inheritDoc}
	 * @param formalPar {@inheritDoc}
	 * @return {@inheritDoc}
	 */
	@Override
	public Type calculateType(Type type, FormalPar formalPar, Model model) {
		// same name of formal par
		if (this.getName().toString().compareTo(formalPar.getName().toString())==0){
			return type; // return the new type
		}
		// else return the current formal par
		return this;
	}
	
	/**
	 * Gets the index of the formal parameter in the class.
	 * 
	 * @return index
	 */
	public int getIndex() {
		int index = 0;
		List<FormalPar> list = this.getClassDef().getFormalPars();
		for (FormalPar fp : list) {
			if (fp.equals(this)){
				return index;
			}
			index++;
		}
		return -1;
	}
	
	 /**
     * Calculates the substitutions of formal parameters with types in current
     * type.
     * 
     * @return empty set
     */
    public SortedMap<FormalPar, Type> calculateSubstitutions() {
    	return new TreeMap<FormalPar,Type>(	new Comparator<FormalPar>() {
			@Override
			public int compare(FormalPar fp1, FormalPar fp2){
				return fp1.getIndex()-fp2.getIndex();
			}
			@Override
			public boolean equals(Object o){
				return false;
			}
		});
    }    
} // FormalParameter
